package cn.assumejson.springframework.aop.framework;

import cn.assumejson.springframework.aop.Advisor;
import cn.assumejson.springframework.beans.factory.HierarchicalBeanFactory;
import cn.assumejson.springframework.beans.factory.ListableBeanFactory;
import cn.assumejson.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/12/6 12:02
 */
public class BeanFactoryAdvisorRetrievalHelper {

    private volatile String[] cachedAdvisorBeanNames;

    private final ConfigurableListableBeanFactory beanFactory;

    public BeanFactoryAdvisorRetrievalHelper(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * Determine whether the aspect bean with the given name is eligible.
     * <p>The default implementation always returns {@code true}.
     *
     * @param beanName the name of the aspect bean
     * @return whether the bean is eligible
     */
    protected boolean isEligibleBean(String beanName) {
        return true;
    }

    public List<Advisor> findAdvisorBeans() {
        String[] advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the auto-proxy creator apply to them!
            advisorNames = beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
        if (ArrayUtil.isEmpty(advisorNames)) {
            return new ArrayList<>();
        }

        List<Advisor> advisors = new ArrayList<>();
        for (String advisorName : advisorNames) {
            if (isEligibleBean(advisorName)) {
                Class<?> beanClass = this.beanFactory.getBeanDefinition(advisorName).getBeanClass();
                if (Advisor.class.isAssignableFrom(beanClass)) {
                    advisors.add(this.beanFactory.getBean(advisorName, Advisor.class));
                }
            }
        }
        return advisors;
    }

    public static String[] beanNamesForTypeIncludingAncestors(
            ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {

        Assert.notNull(lbf, "ListableBeanFactory must not be null");
        // 从本容器中找
        String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
        // 从父容器找并放入result
//        if (lbf instanceof HierarchicalBeanFactory) {
//            HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
//            if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
//                String[] parentResult = beanNamesForTypeIncludingAncestors(
//                        (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
//                result = mergeNamesWithParent(result, parentResult, hbf);
//            }
//        }
        return result;
    }
}
